package Q_Learning;

public class Agent {
	private double alpha;
	private double gamma;
	private double random;
	private int accuReward;
	private Qtable qtable;
	private int x;
	private int y;
	private Action action;
	private GridWorld world;
	public Agent( double a, double g, double r){
		setAlpha(a); // between 0 and 1
		setGamma(g); // between 0 and 1
		setRandom(r); // between 0 and 1
		qtable = new Qtable();
		qtable.printQ();
		world = new GridWorld();
		setPosition( 0, 0 );
		world.generate();
		//world.printAllGrid();		/* for testing*/
		accuReward = 0;
	}
	public void learn(int times) {
		int i;
		for ( i = 0 ; i < times ; i++ ){
			walk(i);
			
		}
		System.out.println("Learning is done.");
		qtable.printQ();
	}
	public void test(){
		int counter = 0;
		/*double r;
		r = random;
		random = 0.0;*/
		setPosition( 0, 0 );
		accuReward = 0;
		while( accuReward != 1 ){//&& accuReward != -1 ){
			walk(-1);
			counter++;
			qtable.printQ();
		}
		MainQLearning.inst.setlog(String.format("Used time: %d, Reward: %d\n", counter, accuReward ) );
		//random = r;
	}
	public void walk(int i) {
		// At first decide which to do, random or Q?
		
		double r;
		r = Math.random();
		updateQ();
		if ( r > random ){
			// Q
			action = qtable.getBestAction( x, y );
			act();
			
		} 
		else {
			// random
			action = qtable.getRandAction( x, y );
			act();
		}
		accuReward += world.getReward(x, y, i);
		
	}
	public void act() {
		x = x + action.xDis;
		y = y + action.yDis;
	}
	public void updateQ() {
		double newQ, currentQ;
		int newx, newy;
		int reward;
		reward = world.getReward(x, y, -1);
		for ( Action a  : Action.values() ){
			newx = x + a.xDis;
			newy = y + a.yDis;
			if ( qtable.checkBoundary( newx, newy )) {
				currentQ = qtable.getQ(x, y, a.type);
				newQ = (1-alpha) * currentQ + 
			    	alpha * ( reward + gamma * qtable.getMaxQ(newx, newy));
			}
			else {
				newQ = Double.NEGATIVE_INFINITY;
			}
				
			qtable.setQ( x, y, a.type, newQ );
		}
	}
	public void setPosition( int x, int y ){
		this.x = x;
		this.y = y;
	}
	public int[] getPosition(){
		int[] ret = { x, y };
		return ret;
	}
	public void setAlpha(double alpha) {
		this.alpha = alpha;
	}
	public double getAlpha() {
		return alpha;
	}
	public void setGamma(double gamma) {
		this.gamma = gamma;
	}
	public double getGamma() {
		return gamma;
	}
	public void setRandom(double random) {
		this.random = random;
	}
	public double getRandom() {
		return random;
	}
	
}
